#include "image.h"
#include <QDebug>

Image::Image() { }
Image::Image(const Image& img)
{
    if(filehead == nullptr)
    {
        filehead = reinterpret_cast<BITMAPFILEHEADER*>(malloc(sizeof(BITMAPFILEHEADER)));
    }
    if(infohead == nullptr)
    {
        infohead = reinterpret_cast<BITMAPINFOHEADER*>(malloc(sizeof(BITMAPINFOHEADER)));
    }
    filehead = img.filehead;
    infohead = img.infohead;
    width = img.getWidth();
    primaryWidth = img.getPrimaryWidth();
    height = img.getHeight();
    if(data == nullptr)
    {
        data = new unsigned char[width * height * 3];
    }
    for(int i = 0; i < height; i++)
    {
        for(int j = 0; j < width; j++)
        {
            *getPixel(data, i, j) = *(img.getPixel(img.getData(), i, j));
            *(getPixel(data, i, j) + 1) = *(img.getPixel(img.getData(), i, j) + 1);
            *(getPixel(data, i, j) + 2) = *(img.getPixel(img.getData(), i, j) + 2);
        }
    }
}

void Image::ReadBMP(const char* ImageName)
{
    //文件打开
    QFile file(ImageName);
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "open failed";
        exit(0);
    }
    //读取文件头数据
    if(filehead == nullptr)
    {
        filehead = reinterpret_cast<BITMAPFILEHEADER*>(malloc(sizeof(BITMAPFILEHEADER)));
    }
    if(infohead == nullptr)
    {
        infohead = reinterpret_cast<BITMAPINFOHEADER*>(malloc(sizeof(BITMAPINFOHEADER)));
    }

    file.read(reinterpret_cast<char*>(&filehead->bfType), 2);
    file.read((char*)&filehead->bfSize, 4);
    file.read((char*)&filehead->bfReserved1, 2);
    file.read((char*)&filehead->bfReserved2, 2);
    file.read((char*)&filehead->bfOffBits, 4);
    file.read((char*)&infohead->biSize, 4);
    file.read((char*)&infohead->biWidth, 4);
    width = infohead->biWidth;
    file.read((char*)&infohead->biHeight, 4);
    height = infohead->biHeight;
    file.read((char*)&infohead->biPlanes, 2);
    file.read((char*)&infohead->biBitCount, 2);
    file.read((char*)&infohead->biCompression, 4);
    file.read((char*)&infohead->biSizeImage, 4);
    file.read((char*)&infohead->biXPelsPerMeter, 4);
    file.read((char*)&infohead->biYPelsPerMeter, 4);
    file.read((char*)&infohead->biClrUsed, 4);
    file.read((char*)&infohead->biClrImportant, 4);
    //补0
    primaryWidth = width;
    if (width % 4 != 0)
    {
        width = (width * 3 + 3) / 4 * 4;
        width = width / 3;
    }
    if(data == nullptr)
    {
        data = new unsigned char [height * width * 3];
    }
    for(int i = height - 1; i >= 0; i--)
    {
        for(int j = 0; j < width; j++)
        {
            file.read(reinterpret_cast<char*>(getPixel(data, i, j) + 2), 1);
            file.read(reinterpret_cast<char*>(getPixel(data, i, j) + 1), 1);
            file.read(reinterpret_cast<char*>(getPixel(data, i, j)), 1);
        }
    }
    file.close();
}

void Image::WriteBMP(const char* filename)
{
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        qDebug() << "open failed";
        exit(0);
    }

    file.write(reinterpret_cast<char*>(&filehead->bfType), 2);
    file.write((char*)&filehead->bfSize, 4);
    file.write((char*)&filehead->bfReserved1, 2);
    file.write((char*)&filehead->bfReserved2, 2);
    file.write((char*)&filehead->bfOffBits, 4);
    file.write((char*)&infohead->biSize, 4);
    file.write((char*)&infohead->biWidth, 4);
    file.write((char*)&infohead->biHeight, 4);
    file.write((char*)&infohead->biPlanes, 2);
    file.write((char*)&infohead->biBitCount, 2);
    file.write((char*)&infohead->biCompression, 4);
    file.write((char*)&infohead->biSizeImage, 4);
    file.write((char*)&infohead->biXPelsPerMeter, 4);
    file.write((char*)&infohead->biYPelsPerMeter, 4);
    file.write((char*)&infohead->biClrUsed, 4);
    file.write((char*)&infohead->biClrImportant, 4);

    for(int i = height - 1; i >= 0; i--)
    {
       for(int j = 0; j < width; j++)
        {
            file.write(reinterpret_cast<char*>(getPixel(data, i, j) + 2), 1);
            file.write(reinterpret_cast<char*>(getPixel(data, i, j) + 1), 1);
            file.write(reinterpret_cast<char*>(getPixel(data, i, j)), 1);
        }
    }
    file.close();
}

unsigned char * Image::getPixel (unsigned char * pixData, int row, int col) const
{
    return pixData + row * width * 3 + col * 3;
}

int Image::getWidth() const
{
    return width;
}

int Image::getPrimaryWidth() const
{
    return primaryWidth;
}

int Image::getHeight() const
{
    return height;
}

unsigned char * Image::getData() const
{
    return data;
}

void Image::LaplacainSharpening()
{
    int laplacain[3][3] = {
        {0, -1, 0},
        {-1, 5, -1},
        {0, -1, 0}
    };
    unsigned char * temp = new unsigned char[height * width * 3];
    int red = 0, green = 0, blue = 0;
    for(int x = 0; x < height; x++)
    {
        for(int y = 0; y < width; y++)
        {
            if(x > 0 && x < height - 1 && y > 0 && y < width - 1)
            {
                red = green = blue = 0;
                for(int i = 0; i < 3; i++)
                {
                    for(int j = 0; j < 3; j++)
                    {
                        red += *getPixel(this->data, x+i-1, y+j-1) * laplacain[i][j];
                        green += *(getPixel(this->data, x+i-1, y+j-1) + 1) * laplacain[i][j];
                        blue += *(getPixel(this->data, x+i-1, y+j-1) + 2) * laplacain[i][j];
                    }
                }
            }
            else
            {
                *getPixel(temp, x, y) = *getPixel(this->data, x, y);
                *(getPixel(temp, x, y) + 1) = *(getPixel(this->data, x, y) + 1);
                *(getPixel(temp, x, y) + 2) = *(getPixel(this->data, x, y) + 2);
                continue;
            }

            if(red < 0) { *getPixel(temp, x, y) = 0; }
            else if(red > 255) { *getPixel(temp, x, y) = 255; }
            else { *getPixel(temp, x, y) = red; }

            if(green < 0) { *(getPixel(temp, x, y) + 1) = 0; }
            else if(green > 255) { *(getPixel(temp, x, y) + 1) = 255; }
            else { *(getPixel(temp, x, y) + 1) = green; }

            if(blue < 0) { *(getPixel(temp, x, y) + 2) = 0; }
            else if(blue > 255) { *(getPixel(temp, x, y) + 2) = 255; }
            else { *(getPixel(temp, x, y) + 2) = blue; }
        }
    }
    delete data;
    data = reinterpret_cast<unsigned char*>(temp);
}

void Image::High_passFilter()
{
    int H2[3][3] = {
        {-1, -1, -1},
        {-1, 8, -1},
        {-1, -1, -1}
    };
    unsigned char * temp = new unsigned char[height * width * 3];
    int red = 0, green = 0, blue = 0;
    for(int x = 0; x < height; x++)
    {
        for(int y = 0; y < width; y++)
        {
            if(x > 0 && x < height - 1 && y > 0 && y < width - 1)
            {
                red = green = blue = 0;
                for(int i = 0; i < 3; i++)
                {
                    for(int j = 0; j < 3; j++)
                    {
                        red += *getPixel(this->data, x+i-1, y+j-1) * H2[i][j];
                        green += *(getPixel(this->data, x+i-1, y+j-1) + 1) * H2[i][j];
                        blue += *(getPixel(this->data, x+i-1, y+j-1) + 2) * H2[i][j];
                    }
                }
            }
            else
            {
                *getPixel(temp, x, y) = *getPixel(this->data, x, y);
                *(getPixel(temp, x, y) + 1) = *(getPixel(this->data, x, y) + 1);
                *(getPixel(temp, x, y) + 2) = *(getPixel(this->data, x, y) + 2);
                continue;
            }
            if(red < 0) { *getPixel(temp, x, y) = 0; }
            else if(red > 255) { *getPixel(temp, x, y) = 255; }
            else { *getPixel(temp, x, y) = red; }

            if(green < 0) { *(getPixel(temp, x, y) + 1) = 0; }
            else if(green > 255) { *(getPixel(temp, x, y) + 1) = 255; }
            else { *(getPixel(temp, x, y) + 1) = green; }

            if(blue < 0) { *(getPixel(temp, x, y) + 2) = 0; }
            else if(blue > 255) { *(getPixel(temp, x, y) + 2) = 255; }
            else { *(getPixel(temp, x, y) + 2) = blue; }
        }
    }
    delete data;
    data = reinterpret_cast<unsigned char*>(temp);
}

void Image::SobelSharpening()
{
    int HX[3][3] = {{1, 0, -1}, {2, 0, -2}, {1, 0, -1}};
    int HY[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};

    unsigned char * temp = new unsigned char[height * width * 3];

    int redX, greenX, blueX;
    int redY, greenY, blueY;

    for(int x = 0; x < height; x++)
    {
        for(int y = 0; y < width; y++)
        {
            if(x > 0 && x < height - 1 && y > 0 && y < width - 1)
            {
                redX = greenX = blueX = 0;
                redY = greenY = blueY = 0;
                for(int i = 0; i < 3; i++)
                {
                    for(int j = 0; j < 3; j++)
                    {
                        redX += *getPixel(this->data, x+i-1, y+j-1) * HX[i][j];
                        redY += *getPixel(this->data, x+i-1, y+j-1) * HY[i][j];
                        greenX += *(getPixel(this->data, x+i-1, y+j-1) + 1) * HX[i][j];
                        greenY += *(getPixel(this->data, x+i-1, y+j-1) + 1) * HY[i][j];
                        blueX += *(getPixel(this->data, x+i-1, y+j-1) + 2) * HX[i][j];
                        blueY += *(getPixel(this->data, x+i-1, y+j-1) + 2) * HY[i][j];
                    }
                }
            }
            else
            {
                *getPixel(temp, x, y) = *getPixel(this->data, x, y);
                *(getPixel(temp, x, y) + 1) = *(getPixel(this->data, x, y) + 1);
                *(getPixel(temp, x, y) + 2) = *(getPixel(this->data, x, y) + 2);
                continue;
            }

            int R, G, B;
            R = (int)(sqrt(redX * redX * 1.0 + redY * redY * 1.0));
            G = (int)(sqrt(greenX * greenX * 1.0 + greenY * greenY * 1.0));
            B = (int)(sqrt(blueX * blueX * 1.0 + blueY * blueY * 1.0));

            if (redX < 0 && redY < 0) { *getPixel(temp, x, y)=0; }
            else if (R > 255) { *getPixel(temp, x, y)=255; }
            else { *getPixel(temp, x, y)=R; }

            if (greenX<0 && greenY<0) { *(getPixel(temp, x, y) + 1)=0; }
            else if (G > 255) { *(getPixel(temp, x, y) + 1)=255; }
            else { *(getPixel(temp, x, y) + 1)=G; }

            if (blueX<0 && blueY<0 ) { *(getPixel(temp, x, y) + 2)=0; }
            else if (B > 255) { *(getPixel(temp, x, y) + 2)=255; }
            else { *(getPixel(temp, x, y) + 2)=B; }
        }
    }
    delete data;
    data = reinterpret_cast<unsigned char*>(temp);
}

//双线性插值
void Image::resize(int newHeight, int newWidth)
{
    unsigned char * dataScal;
    primaryWidth = newWidth;
    if (newWidth % 4 != 0)
    {
        newWidth = (newWidth * 3 + 3) / 4 * 4;
        newWidth = newWidth / 3;
    }
    dataScal = new unsigned char[newHeight * newWidth * 3];
    int lineByte = width * 3;
    int newLineByte = newWidth * 3;

    float expHScalValue = 0, expWScalValue = 0;
    expHScalValue = (float)newHeight / (float)height;
    expWScalValue = (float)newWidth / (float)width;
    qDebug() << expHScalValue << " " << expWScalValue;
    for (int i = 0; i < newHeight; i++)
    {
        for (int j = 0; j < newWidth; j++)
        {

            float d_original_img_hnum = i / expHScalValue;
            float d_original_img_wnum = j / expWScalValue;
            int i_original_img_hnum = d_original_img_hnum;
            int i_original_img_wnum = d_original_img_wnum;
            float distance_to_a_x = d_original_img_wnum - i_original_img_wnum;//在原图像中与a点的水平距离
            float distance_to_a_y = d_original_img_hnum - i_original_img_hnum;//在原图像中与a点的垂直距离

            int original_point_a = i_original_img_hnum * lineByte + i_original_img_wnum * 3;
            int original_point_b = i_original_img_hnum * lineByte + (i_original_img_wnum + 1) * 3;
            int original_point_c = (i_original_img_hnum + 1) * lineByte + i_original_img_wnum * 3;
            int original_point_d = (i_original_img_hnum + 1) * lineByte + (i_original_img_wnum + 1) * 3;

            if (i_original_img_hnum  == height - 1)
            {
                original_point_c = original_point_a;
                original_point_d = original_point_b;
            }
            if (i_original_img_wnum  == width - 1)
            {
                original_point_b = original_point_a;
                original_point_d = original_point_c;
            }

            int pixel_point = i * newLineByte + j * 3;//映射尺度变换图像数组位置偏移量
            for (int k = 0; k < 3; k++)
            {
                dataScal[pixel_point + k] =
                    data[original_point_a + k] * (1 - distance_to_a_x) * (1 - distance_to_a_y) +
                    data[original_point_b + k] * distance_to_a_x * (1 - distance_to_a_y) +
                    data[original_point_c + k] * distance_to_a_y * (1 - distance_to_a_x) +
                    data[original_point_d + k] * distance_to_a_y * distance_to_a_x;
            }
        }
    }
    delete data;
    data = dataScal;
    height = newHeight;
    width = newWidth;
    infohead->biHeight = newHeight;
    infohead->biWidth = newWidth;
    infohead->biSizeImage = newHeight * newWidth * infohead->biBitCount;
}

void Image::swapPixel(int x1, int y1, int x2, int y2)
{
    double t;
    t = *getPixel(data, x1, y1);
    *getPixel(data, x1, y1) = *getPixel(data, x2, y2);
    *getPixel(data, x2, y2) = t;
    t = *(getPixel(data, x1, y1) + 1);
    *(getPixel(data, x1, y1) + 1) = *(getPixel(data, x2, y2) + 1);
    *(getPixel(data, x2, y2) + 1) = t;
    t = *(getPixel(data, x1, y1) + 2);
    *(getPixel(data, x1, y1) + 2) = *(getPixel(data, x2, y2) + 2);
    *(getPixel(data, x2, y2) + 2) = t;
}

void Image::FilpUpAndDown()
{
    for(int j = 0; j < width; j++)
    {
        for(int i = 0; i < height / 2; i++)
        {
            swapPixel(i, j, height - i - 1, j);
        }
    }

}

void Image::FilpLeftAndRight()
{
    for(int i = 0; i < height; i++)
    {
        for(int j = 0; j < width / 2; j++)
        {
            swapPixel(i, j, i, width - j - 1);
        }
    }
}

double * Image::average()
{
    double * means = new double[3];
    double sumR = 0, sumG = 0, sumB = 0;
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            sumB += *getPixel(data, i , j);
            sumG += *(getPixel(data, i, j) + 1);
            sumR += *(getPixel(data, i, j) + 2);
        }
    }
    means[0] = sumR / (height * width);
    means[1] = sumG / (height * width);
    means[2] = sumB / (height * width);
    return means;
}

double * Image::variance()
{
    double * variances = new double[3];
    double * m = average();
    double sumR = 0, sumG = 0, sumB = 0;
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            sumB += (*getPixel(data, i, j) - m[0]) * (*getPixel(data, i, j) - m[0]);
            sumG += (*(getPixel(data, i, j) + 1) - m[1]) * (*(getPixel(data, i, j) + 1) - m[1]);
            sumR += (*(getPixel(data, i, j) + 2) - m[2]) * (*(getPixel(data, i, j) + 2) - m[2]);
        }
    }
    variances[0] = sqrt(sumR) / (height * width);
    variances[1] = sqrt(sumG) / (height * width);
    variances[2] = sqrt(sumB) / (height * width);
    delete[] m;
    return variances;
}

void Image::Gaussian_Blur(double sigma)
{
    //图像副本
    unsigned char * temp = new unsigned char[height * width * 3];
    //获取高斯模板
    double dGaussianTemplate[3][3];
    int center = 1;
    double x = 0, y = 0;
    for(int i = 0; i < 3; i++)
    {
        x = pow(i - center, 2);
        for(int j = 0; j < 3; j++)
        {
            y = pow(j - center, 2);
            double g = exp(-(x + y) / (2 * sigma * sigma));
            g /= 2 * 3.1415926535 * sigma;
            dGaussianTemplate[i][j] = g;
        }
    }
        //归一化处理
    double t = 1 /dGaussianTemplate[0][0];
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            dGaussianTemplate[i][j] *= t;
        }
    }
    int *gaussianTemplate = new int[3 * 3];
    unsigned char templateSum = 0;
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            gaussianTemplate[i + j * 3] = floor(dGaussianTemplate[i][j]);
            templateSum += floor(dGaussianTemplate[i][j]);
        }
    }

    //卷积
    for(int i = 0; i < height; i++)
    {
        for(int j = 0; j < width; j++)
        {
            //边缘像素不作处理
            if(i == 0 || i == height - 1 || j == 0 || j == width - 1)
            {
                *getPixel(temp, i, j) = *getPixel(this->data, i, j);
                *(getPixel(temp, i, j) + 1) = *(getPixel(this->data, i ,j) + 1);
                *(getPixel(temp, i, j) + 2) = *(getPixel(this->data, i, j) + 2);
                continue;
            }
            //非边缘像素
            else
            {
                //R
                int newRed = 0;
                QVector<int> tempRed;
                for(int m = i - 1; m < i + 2; m++)
                {
                    for(int n = j - 1; n < j + 2; n++)
                    {
                        tempRed.push_back(*getPixel(data, m, n));
                    }
                }
                for(int k = 0; k < 9; k++)
                {
                    newRed += gaussianTemplate[k] * tempRed[k];
                }
                newRed /= templateSum;
                //qDebug() << newRed;
                //G
                int newGreen = 0;
                QVector<int> tempGreen;
                for(int m = i - 1; m < i + 2; m++)
                {
                    for(int n = j - 1; n < j + 2; n++)
                    {
                        tempGreen.push_back(*(getPixel(data, m, n) + 1));
                    }
                }
                for(int k = 0; k < 9; k++)
                {
                    newGreen += gaussianTemplate[k] * tempGreen[k];
                }
                newGreen /= templateSum;
                //qDebug() << newGreen;
                //B
                int newBlue = 0;
                QVector<int> tempBlue;
                for(int m = i - 1; m < i + 2; m++)
                {
                    for(int n = j - 1; n < j + 2; n++)
                    {
                        tempBlue.push_back(*(getPixel(data, m, n) + 2));
                    }
                }
                for(int k = 0; k < 9; k++)
                {
                    newBlue += gaussianTemplate[k] * tempBlue[k];
                }
                newBlue /= templateSum;
                //qDebug() << newBlue;
                //卷积结果写入副本
                *getPixel(temp, i, j) = newRed;
                *(getPixel(temp, i, j) + 1) = newGreen;
                *(getPixel(temp, i, j) + 2) = newBlue;
            }
        }
    }
    delete data;
    data = reinterpret_cast<unsigned char*>(temp);
}

void Image::ToGray()
{
    for(int i = 0; i < height; i++)
    {
        for(int j = 0; j < width; j++)
        {
            double t = 0.3 * (*getPixel(data, i, j)) + 0.59 * (*(getPixel(data, i, j) + 1))
                    + 0.11 * (*(getPixel(data, i, j) + 2));
            *getPixel(data, i, j) = t;
            *(getPixel(data, i, j) + 1) = t;
            *(getPixel(data, i, j) + 2) = t;
        }
    }
}

void Image::Binarization()
{
    //转为灰度图
    ToGray();
    //选取128作为全局阈值
    for(int i = 0; i < height; i++)
    {
        for(int j = 0; j < width; j++)
        {
            if(*getPixel(data, i, j) > 128)
            {
                *getPixel(data, i, j) = 255;
                *(getPixel(data, i, j) + 1) = 255;
                *(getPixel(data, i, j) + 2) = 255;
            }
            else
            {
                *getPixel(data, i, j) = 0;
                *(getPixel(data, i, j) + 1) = 0;
                *(getPixel(data, i, j) + 2) = 0;
            }
        }
    }
}

void Image::Contour_Detection()
{
    //灰度图
    unsigned char * newImg = new unsigned char[height * width * 3];
    for(int i = 0; i < height * width * 3; i++)
        newImg[i] = 255;
    this->Binarization();
    //当前像素周围的像素
    int pixel[8];
    //轮廓提取
    for(int i = 1; i < height - 1; i++)
    {
        for(int j = 1; j < width - 1; j++)
        {
            if(*getPixel(data, i, j) == 0)
            {
                memset(pixel, 0, 8);
                *getPixel(newImg, i, j) = 0;
                *(getPixel(newImg, i, j) + 1) = 0;
                *(getPixel(newImg, i, j) + 2) = 0;
                pixel[0] = *getPixel(data, i - 1, j - 1);
                pixel[1] = *getPixel(data, i - 1, j);
                pixel[2] = *getPixel(data, i - 1, j + 1);
                pixel[3] = *getPixel(data, i, j - 1);
                pixel[4] = *getPixel(data, i, j + 1);
                pixel[5] = *getPixel(data, i + 1, j - 1);
                pixel[6] = *getPixel(data, i + 1, j);
                pixel[7] = *getPixel(data, i + 1, j + 1);

                if (pixel[0] + pixel[1] + pixel[2] + pixel[3] + pixel[4] + pixel[5] + pixel[6] + pixel[7] == 0)
                {
                    *getPixel(newImg, i, j) = 255;
                    *(getPixel(newImg, i, j) + 1) = 255;
                    *(getPixel(newImg, i, j) + 2) = 255;
                }
            }
        }
    }
    delete data;
    data = reinterpret_cast<unsigned char*>(newImg);
}
